<script setup lang="ts">
import Designer from "./Designer.vue";
import useStore from "../../store/index";
import ControlConfig from "./ControlConfig.vue";
import { FormDataEnum } from "../../store/modules/FormData";
import { computed, ref, watch, ComputedRef, Ref } from "vue";
import { formSettingItems } from "./config/FormSettingItems";
import { FormItemsEnum } from "../../store/modules/FormItems";
import {
  CheckboxType,
  ColorType,
  InputNumberType,
  InputType,
  PasswordType,
  RadioType,
  RateType,
  SelectType,
  SliderType,
  SwitchType,
  TextareaType,
  TimePickerType,
  TimeSelectType,
  VirtualizedSelectType,
} from "../../types/ItemMetaTypes";
import { ElMessage } from "element-plus";
import ItemResolve from "./ItemsResolve.vue";

interface PropsType {
  height?: string;
  preview?: boolean;
  formData?: any;
  formItems?: Array<
    | CheckboxType
    | InputType
    | PasswordType
    | TextareaType
    | InputNumberType
    | RadioType
    | RateType
    | SwitchType
    | SliderType
    | ColorType
    | SelectType
    | VirtualizedSelectType
    | TimePickerType
    | TimeSelectType
  >;
}

// eslint-disable-next-line no-undef
const props = withDefaults(defineProps<PropsType>(), {
  preview: false,
  height: "96.8vh",
  formData: useStore.state.FormData,
  // @ts-ignore
  formItems: useStore.state.FormItems.items,
});

// eslint-disable-next-line no-undef
const emit = defineEmits(["submit"]);

// 提交
const submit = (): void => {
  emit("submit");
};

// 在默认情况下不重新设置vuex中的数据,以防循环依赖
if (props.formItems !== useStore.state.FormItems.items) {
  // 设置vuex中的formItems
  useStore.dispatch(
    FormItemsEnum.ModuleFunctionName(FormItemsEnum.RESET_FORM_ITEMS),
    props.formItems
  );
}
let storeFormItems = computed(() => useStore.state.FormItems.items);

// 在默认情况下不重新设置vuex中的数据,以防循环依赖
if (props.formData !== useStore.state.FormData) {
  // 设置vuex中的formData
  useStore.dispatch(
    FormDataEnum.ModuleFunctionName(FormDataEnum.RESET_FORM_DATA),
    props.formData
  );
}
let storeFormData = computed(() => useStore.state.FormData.data);

// 重新整理数据
let sideFormItems: any = {};
for (let key in useStore.state) {
  if (key.includes("/")) {
    let keyArr: Array<string> = key.split("/");
    if (!sideFormItems[keyArr[0]]) {
      sideFormItems[keyArr[0]] = [];
    }
    // @ts-ignore
    sideFormItems[keyArr[0]].push(useStore.state[key]);
  }
}

// 将其它放在最后
const tempOtherItems = sideFormItems.otherItems;
delete sideFormItems.otherItems;
sideFormItems.otherItems = tempOtherItems;

// 要预览的代码
const previewCodes = ref("");
// 显示预览
const dialogPreview = ref(false);
// 显示代码预览
const dialogCodePreview = ref(false);
// 显示表单数据
const dialogGetFormData = ref(false);
// 显示列表数据
const dialogGetListData = ref(false);
// 显示表单配置JSON
const dialogGetFormConfig = ref(false);
// 显示表单配置JSON
const dialogImportJson = ref(false);
// 右侧设置tab当前选中
const activeName = ref("controlSetting");
// 右侧设置tab当前选中
const importJsonData = ref("");
// 右侧设置tab当前选中
const selectItem: ComputedRef<any> = computed(() => useStore.state.FormItems.currentItem);
// 右侧设置tab当前选中
const changeItem: ComputedRef<boolean> = computed(
  () => useStore.state.FormItems.changeItem
);

// 复制默认的表单配置，作为右侧设置表单的配置
const formSetting = computed(() => useStore.state.FormConfig);
const formConfig = JSON.parse(JSON.stringify(formSetting.value));

interface KeyTexts {
  baseItems: string;
  otherItems: string;
  selectItems: string;
}

const keyTexts: KeyTexts = {
  baseItems: "基础控件",
  otherItems: "其它控件",
  selectItems: "控件选择",
};

// 获取表单数据
const getFormData = () => {
  dialogGetFormData.value = true;
};

// 获取表单项列表json
const getListData = () => {
  dialogGetListData.value = true;
};

// 获取表单配置
const getFormConfig = () => {
  dialogGetFormConfig.value = true;
};

// 预览表单
const previewForm = () => {
  dialogPreview.value = true;
};

// 预览表单代码
const previewCode = () => {
  dialogCodePreview.value = true;
  previewCodes.value = getPreviewCode();
};

const downloadForm = () => {
  let urlObject = window.URL || window.webkitURL || window;
  previewCodes.value = getPreviewCode();
  let export_blob = new Blob([previewCodes.value]);
  let save_link: any = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
  save_link.href = urlObject.createObjectURL(export_blob);
  save_link.download = "FormDesigner.vue";
  let ev = document.createEvent("MouseEvents");
  ev.initMouseEvent(
    "click",
    true,
    false,
    window,
    0,
    0,
    0,
    0,
    0,
    false,
    false,
    false,
    false,
    0,
    null
  );
  save_link.dispatchEvent(ev);
};

// 获取预览代码
const getPreviewCode = (): string => {
  /* eslint-disable */
  return `
<script setup lang="ts">
    import { ref } from 'vue'
    const formData = ref(${JSON.stringify(storeFormData.value)})
    const formItems = ref(${JSON.stringify(storeFormItems.value)})
    const formSettings = ref(${JSON.stringify(formSetting.value)})
    // 提交事件需添加提交类型的按钮方可触发(触发方式：@click)
    const submit = () => {}
<\/script>
<template>
    <Designer :formItems="formItems" :preview="true" :formData="formData" :formConfig="formSettings" @submit="submit" />
<\/template>
        `;
  /* eslint-enable */
};

// 点击某个表单项时触发
const getFormItem = () => {
  // 如果是切换组件，则暂时屏蔽监听
  useStore.dispatch(FormItemsEnum.ModuleFunctionName(FormItemsEnum.RELOAD_CHANGE_ITEM));
};

// 监听modelValue的变化
watch(
  () => selectItem.value.modelValue,
  (newVal, oldVal) => {
    if (changeItem.value && newVal) {
      storeFormData.value[newVal] = storeFormData.value[oldVal];
      delete storeFormData.value[oldVal];
    }
  }
);

const designer: Ref<HTMLElement | null> = ref<HTMLElement | null>(null);

// 获取表单实例
const getDynamicForm = (): HTMLElement | null => {
  // @ts-ignore
  return designer.value.dynamicForm;
};

// eslint-disable-next-line no-undef
defineExpose({
  getDynamicForm,
});

// 清空
const clearData = () => {
  // @ts-ignore
  // eslint-disable-next-line vue/no-mutating-props
  props.formItems.length = 0;
  useStore.dispatch(FormItemsEnum.ModuleFunctionName(FormItemsEnum.SET_CURRENT_ITEM), {});
  for (let key in storeFormData.value) {
    // eslint-disable-next-line no-prototype-builtins
    if (storeFormData.value.hasOwnProperty(key)) {
      delete storeFormData.value[key];
    }
  }
};

// 导入列
const importData = () => {
  let data = [];
  try {
    data = JSON.parse(importJsonData.value);
    // eslint-disable-next-line no-empty
  } catch (e) {}
  if (Array.isArray(data)) {
    data.forEach((e) => {
      if (e.type === "Checkbox") {
        initItemData({ key: e.modelValue, value: [] });
      }
      if (e.type === "InputNumber") {
        initItemData({ key: e.modelValue, value: 0 });
      }
      storeFormItems.value.push(e);
    });
  } else {
    ElMessage.error("请输入数组格式的JSON");
  }
  dialogImportJson.value = false;
  importJsonData.value = "";
};

// 初始化表单的特殊字段双向绑定属性
const initItemData = (e: any): void => {
  // @ts-ignore
  storeFormData[e.key] = e.value;
  if (!e.key) {
    return;
  }
  // 移除原先存在的key
  let keys: Array<string> = e.key.split("_");
  if (!keys || keys.length !== 2) {
    return;
  }
  // @ts-ignore
  delete storeFormData[keys[0]];
};

// 导入列
const onImportInput = () => {
  if (importJsonData.value === "") {
    return;
  }
  try {
    importJsonData.value = JSON.stringify(JSON.parse(importJsonData.value), null, 4);
  } catch (e) {
    ElMessage.error("输入json格式有误，请检查后重新输入");
  }
};
</script>

<template>
  <el-row :gutter="0" :style="{ height }">
    <el-col :span="6">
      <el-card shadow="never" class="container-left">
        <el-scrollbar :height="height">
          <el-form :aria-readonly="true" ref="dynamicForm" label-position="top">
            <div v-for="(item, key, i) in sideFormItems" :key="key + i">
              <div class="form-item-title">
                {{ keyTexts[key] }}
              </div>

              <draggable
                :sort="false"
                :list="item"
                :group="{
                  name: 'people',
                  pull: 'clone',
                  put: false,
                }"
                class="form-item-block"
              >
                <div
                  v-for="(item1, i1) in item"
                  :key="item1.modelValue"
                  class="control-block"
                >
                  <label
                    class="el-form-item__label"
                    v-if="keyTexts[key] === '其它控件'"
                    >{{ item1.label }}</label
                  >
                  <ItemResolve
                    :formItem="item1"
                    :preview="true"
                    :formData="{
                      Checkbox: [],
                      InputNumber: 0,
                    }"
                    :dynamicForm="null"
                  />
                </div>
              </draggable>
            </div>
          </el-form>
        </el-scrollbar>
      </el-card>
    </el-col>
    <el-col :span="12">
      <el-card shadow="never" class="container-left generator-container">
        <div class="generator-tool-bar">
          <div class="generator-title">编辑区</div>
          <div>
            <el-button type="text" @click="clearData">清空</el-button>
            <el-button type="text" @click="dialogImportJson = true">导入</el-button>
            <el-button type="text" @click="downloadForm">下载</el-button>
            <el-button type="text" @click="previewForm">预览</el-button>
            <el-button type="text" @click="previewCode">代码预览</el-button>
            <el-button type="text" @click="getFormConfig">表单配置</el-button>
            <el-button type="text" @click="getFormData">表单数据</el-button>
            <el-button type="text" @click="getListData">表单项列表</el-button>
          </div>
        </div>
        <el-scrollbar
          height="100%"
          style="
            padding: var(--el-card-padding) var(--el-card-padding) 0
              var(--el-card-padding);
          "
        >
          <Designer
            ref="designer"
            :formItems="storeFormItems"
            :height="height"
            :preview="preview"
            :formData="storeFormData"
            :formConfig="formSetting"
            @submit="submit"
            @selectItem="getFormItem"
          />
        </el-scrollbar>
      </el-card>
    </el-col>
    <el-col :span="6">
      <el-card shadow="never" class="container-left generator-container">
        <el-tabs v-model="activeName">
          <el-tab-pane label="控件设置" name="controlSetting">
            <el-scrollbar
              style="
                padding: var(--el-card-padding) var(--el-card-padding) 0
                  var(--el-card-padding);
              "
            >
              <ControlConfig :style="{ height: `calc(${height} - 70px)` }" />
            </el-scrollbar>
          </el-tab-pane>
          <el-tab-pane
            label="表单设置"
            name="formSetting"
            style="
              padding: var(--el-card-padding) var(--el-card-padding) 0
                var(--el-card-padding);
            "
          >
            <Designer
              :formItems="formSettingItems"
              :height="`calc(${height} - 50px)`"
              :preview="true"
              :formData="formSetting"
              :formConfig="formConfig"
            />
          </el-tab-pane>
        </el-tabs>
      </el-card>
    </el-col>
  </el-row>
  <el-dialog
    v-model="dialogPreview"
    title="Preview"
    fullscreen
    destroy-on-close
    append-to-body
  >
    <Designer
      :formItems="storeFormItems"
      :height="height"
      :preview="true"
      :formData="storeFormData"
      :formConfig="formSetting"
    />
  </el-dialog>
  <el-dialog
    v-model="dialogCodePreview"
    title="Preview"
    fullscreen
    destroy-on-close
    append-to-body
  >
    <pre v-highlightjs="previewCodes"><code class="autodetect"></code></pre>
  </el-dialog>
  <el-dialog
    v-model="dialogGetFormData"
    title="formData"
    fullscreen
    destroy-on-close
    append-to-body
  >
    <json-viewer :value="storeFormData" copyable boxed sort />
  </el-dialog>
  <el-dialog
    v-model="dialogGetListData"
    title="listData"
    fullscreen
    destroy-on-close
    append-to-body
  >
    <json-viewer :value="storeFormItems" copyable boxed sort />
  </el-dialog>
  <el-dialog
    v-model="dialogGetFormConfig"
    title="formSetting"
    fullscreen
    destroy-on-close
    append-to-body
  >
    <json-viewer :value="formSetting" copyable boxed sort />
  </el-dialog>
  <el-dialog v-model="dialogImportJson" title="输入表单列JSON" width="30%" append-to-body>
    <span
      ><el-input
        v-model="importJsonData"
        @input="onImportInput"
        :rows="15"
        type="textarea"
        placeholder="Please input"
    /></span>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogImportJson = false">取消</el-button>
        <el-button type="primary" @click="importData">确定</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<style scoped>
.control-block ::v-deep(.el-range-editor--medium.el-input__inner) {
  width: 95%;
}
.control-block {
  margin: 6px;
  padding: 10px 20px;
}
.control-block:hover {
  border-radius: 5px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
::v-deep(.el-tabs__nav-scroll) {
  padding-left: var(--el-card-padding);
}
.form-item-title {
  font-size: 15px;
  font-weight: bold;
  margin: 3% 3% 5% 0;
}

.form-item-block {
  width: 100%;
  /*display: flex;
    flex-wrap: wrap;
    align-content: flex-start;*/
}

.generator-title {
  font-size: 15px;
  font-weight: bold;
}

.generator-container ::v-deep(.el-card__body) {
  padding: 0 !important;
}

.generator-tool-bar {
  height: 39px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--el-card-padding);
  border-bottom: 1px solid #dcdfe6;
}

.el-card {
  margin: 0;
  border-radius: 0;
  padding-bottom: 0;
}

.el-row {
  margin: 0 !important;
}

::v-deep(.el-card__body) {
  padding-bottom: 0;
}

.el-col {
  padding: 0;
}

.el-row:last-child {
  margin-bottom: 0;
}

.container-left {
  height: 100%;
}

.el-form .width-1 .sortable-chosen {
  width: 100%;
}

.el-form .width-2 .sortable-chosen {
  width: 50%;
}

.el-form .width-3 .sortable-chosen {
  width: 33.33%;
}

.el-form .width-4 .sortable-chosen {
  width: 25%;
}

.el-form .width-5 .sortable-chosen {
  width: 20%;
}

.el-form .width-6 .sortable-chosen {
  width: 16.66%;
}

/*.el-form .sortable-chosen {
    width: 100%;
    font-size: 0;
    border: none;
    line-height: 0;
    overflow: hidden;
    border-top: 1px dashed var(--el-color-primary);
}*/

.form-item-control {
  width: 45%;
  cursor: move;
  line-height: 30px;
  margin: 0 3% 3% 0;
  text-align: center;
  font-size: var(--el-font-size-base);
  border: 1px solid #dcdfe6;
}

.form-item-control:hover {
  color: var(--el-color-primary);
  border-color: var(--el-color-primary);
}

.container-left .container-item {
  min-height: 20px;
  padding: 20px 0 !important;
  border: 1px dashed #dcdfe6 !important;
}

.container-left ::v-deep(.container-item .el-col) {
  border: 1px dashed #dcdfe6 !important;
}
</style>
